package com.nervenets.template.controller.user;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.exceptions.ClientException;
import com.nervenets.general.Global;
import com.nervenets.general.annotation.ProhibitDuplicateRequest;
import com.nervenets.general.annotation.RateLimiter;
import com.nervenets.general.aspect.SysLog;
import com.nervenets.general.entity.ResponseResult;
import com.nervenets.general.enumeration.LimitType;
import com.nervenets.general.jwt.aspect.JwtRole;
import com.nervenets.general.jwt.aspect.JwtSecurity;
import com.nervenets.general.jwt.util.JwtUtils;
import com.nervenets.general.redis.RedisSyncLocker;
import com.nervenets.general.service.GlobalSecurityService;
import com.nervenets.general.service.RedisService;
import com.nervenets.general.utils.*;
import com.nervenets.general.web.BaseEntityController;
import com.nervenets.general.web.params.BoolIdParams;
import com.nervenets.template.controller.user.dto.TouristsLoginDto;
import com.nervenets.template.controller.user.dto.UserLoginDto;
import com.nervenets.template.controller.user.params.*;
import com.nervenets.template.enumeration.*;
import com.nervenets.template.exception.UserNotLoginException;
import com.nervenets.template.hibernate.domain.user.User;
import com.nervenets.template.service.AliyunService;
import com.nervenets.template.service.RoleGroupService;
import com.nervenets.template.service.UserService;
import com.nervenets.template.service.WealthFlowService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.Arrays;
import java.util.Map;

@Api(value = "用户接口v1", tags = "用户相关接口")
@RestController
@RequestMapping("/api/user")
@JwtRole(group = "users", groupName = "用户管理", function = "user", functionName = "用户管理", queue = 10)
public class UserController extends BaseEntityController<User, UserService, UserFormParams, UsersPagingParams, User, User> {
    @Autowired
    private RedisSyncLocker<ResponseEntity<ResponseResult<UserLoginDto>>> userLoginRedisSyncLocker;
    @Autowired
    private RedisSyncLocker<ResponseEntity<ResponseResult<User>>> userRedisSyncLocker;
    @Autowired
    private RedisSyncLocker<ResponseEntity<ResponseResult<CommonResponse>>> mobileCodeRedisSyncLocker;
    @Resource
    private RedisService redisService;
    @Resource
    private AliyunService aliyunService;
    @Resource
    private WealthFlowService wealthFlowService;
    @Resource
    private GlobalSecurityService globalSecurityService;
    @Resource
    private RoleGroupService roleGroupService;

    @Override
    public ResponseEntity<ResponseResult<User>> edit(@Valid @RequestBody UserFormParams params, HttpServletRequest request, HttpServletResponse response) {
        return super.edit(params, request, response);
    }

    @Override
    protected User generateDetailResult(User user, HttpServletRequest request, HttpServletResponse response) {
        user.setSurplus(getUserSurplus(user.getId()));
        return user;
    }

    @ApiOperation(value = "访客Token")
    @PostMapping("/tourists")
    @JwtSecurity(required = false)
    @ProhibitDuplicateRequest(message = "不急不急，慢慢请求")
    @RateLimiter(limitType = LimitType.USER, message = "您获取TOKEN的次数太多")
    public ResponseEntity<ResponseResult<TouristsLoginDto>> tourists(HttpServletRequest request) {
        //可以做一些访客限制
        return successMessage(new TouristsLoginDto(JwtUtils.license(request), globalSecurityService, roleGroupService, request));
    }

    @SysLog
    @ApiOperation(value = "手机号码密码登录")
    @PostMapping("/login")
    @JwtSecurity(required = false)
    public ResponseEntity<ResponseResult<UserLoginDto>> login(@Valid @RequestBody MobileLoginParams params, HttpServletRequest request) {
        final User user = service.findByMobile(params.getUsername());
        if (null == user) return errorMessage(HttpStatus.NOT_FOUND.value(), "账号或密码不正确");
        if (!user.isEnable()) return errorMessage(HttpStatus.NOT_FOUND.value(), "该账号已被禁用");
        if (!params.decryptPassword().equals(user.decryptPassword()))
            return errorMessage("您输入的手机号码或密码不正确");

        return successMessage(new UserLoginDto(user, true, JwtUtils.license(request), globalSecurityService, roleGroupService, request));
    }

    @SysLog
    @ApiOperation(value = "手机号码验证码登录")
    @PostMapping("/login/code")
    @JwtSecurity(required = false)
    public ResponseEntity<ResponseResult<UserLoginDto>> loginByCode(@Valid @RequestBody MobileLoginByCodeParams params, HttpServletRequest request) throws Exception {
        if (!String.valueOf(params.getCode()).equals(redisService.get(String.format(Global.Constants.MOBILE_VALIDATION_CODE, params.getMobile())))) {
            return errorMessage("您填写的验证码不正确或失效！");
        }
        return userLoginRedisSyncLocker.keyLock(params.getMobile(), () -> {
            User user = service.findByMobile(params.getMobile());
            if (null == user) {
                user = User.builder()
                        .mobile(params.getMobile())
                        .name("尾号" + params.getMobile().substring(6))
                        .build();
            }
            if (!user.isEnable()) return errorMessage(HttpStatus.NOT_FOUND.value(), "该账号已被禁用");
            user.setLastLoginTime(JodaUtils.getTimestamp());
            service.save(user);

            return successMessage(new UserLoginDto(user, true, JwtUtils.license(request), globalSecurityService, roleGroupService, request));
        });
    }

    @SysLog
    @ApiOperation(value = "登录信息")
    @PostMapping("/loginInfo")
    public ResponseEntity<ResponseResult<UserLoginDto>> loginInfo(HttpServletRequest request) {
        final User user = service.findOne(JwtUtils.getUser().getId());
        if (null == user) throw new UserNotLoginException();
        return successMessage(new UserLoginDto(user, false, null, globalSecurityService, roleGroupService, request));
    }

    @SysLog
    @ApiOperation("手机号码密码注册")
    @PostMapping("/register")
    @JwtSecurity(required = false)
    public ResponseEntity<ResponseResult<Void>> register(@Valid @RequestBody MobileRegisterParams params) {
        if (!params.getPassword().equals(params.getPassword2())) {
            return errorMessage("两次输入的密码不相同");
        }

        if (!String.valueOf(params.getValidationCode()).equals(redisService.get(String.format(Global.Constants.MOBILE_VALIDATION_CODE, params.getMobile())))) {
            return errorMessage("您填写的验证码不正确或失效！");
        }

        if (params.getInviteUserId() > 0) {
            final User user = service.findById(params.getInviteUserId()).orElse(null);
            if (null == user) return errorMessage("邀请码无效！");
        }

        User user = service.findByMobile(params.getMobile());
        if (null != user) return errorMessage("该手机号码已注册");

        user = new User();
        user.setMobile(params.getMobile());
        user.generateNewPassword(params.getPassword());
        user.setAvatar(params.getAvatar());
        user.setName(params.getName());
        user.setGender(params.getGender());
        user.setInviteUserId(params.getInviteUserId());
        service.save(user);

        return successMessage();
    }

    @SysLog
    @ApiOperation(value = "用户绑定手机号码")
    @PostMapping("/bindMobile")
    public ResponseEntity<ResponseResult<User>> bindMobile(@Valid @RequestBody BindMobileParams params) throws Exception {
        if (!String.valueOf(params.getValidationCode()).equals(redisService.get(String.format(Global.Constants.MOBILE_VALIDATION_CODE, params.getMobile())))) {
            return errorMessage("您填写的验证码不正确或失效！");
        }
        return userRedisSyncLocker.idLock(JwtUtils.getUser().getId(), () -> {
            final User user = service.findOne(JwtUtils.getUser().getId());
            if (null == user) return errorMessage(HttpStatus.NOT_FOUND.value(), "token无效，当前用户不存在");
            user.setMobile(params.getMobile());
            service.save(user);
            return successMessage(user);
        });
    }

    @SysLog
    @ApiOperation(value = "验证码发送")
    @PostMapping("/validationCode")
    @JwtSecurity(required = false)
    public ResponseEntity<ResponseResult<CommonResponse>> validationCode(@Valid @RequestBody ValidationCodeMobileParams params) throws Exception {
        if (params.isCheckRegister()) {
            final User user = service.findByMobile(params.getMobile());
            if (null == user) return errorMessage(HttpStatus.NOT_FOUND.value(), "该手机号未注册");
        }

        final String validationCodeKey = String.format(Global.Constants.MOBILE_VALIDATION_CODE, params.getMobile());

        return mobileCodeRedisSyncLocker.keyLock(validationCodeKey, () -> {
            String validationCode = redisService.get(validationCodeKey);
            if (!StringUtils.isEmpty(validationCode))
                return successMessage();

            boolean development = Arrays.toString(SpringContextHolder.getEnvironment().getActiveProfiles()).contains("dev")/*false*/;
            validationCode = development ? "888888" : String.valueOf(NumberUtil.randomNumber(100000, 999999));

            if (development) {
                redisService.set(validationCodeKey, validationCode, 5 * 60);
                return successMessage();
            }
            CommonResponse result;
            try {
                result = aliyunService.sendZhSms(params.getMobile(), validationCode);
            } catch (ClientException e) {
                return errorMessage("短信通道访问失败");
            }
            JSONObject json = JSON.parseObject(result.getData());
            if ("OK".equals(json.getString("Code"))) {
                redisService.set(validationCodeKey, validationCode, 5 * 60);
                return successMessage(result);
            }
            return errorMessage(json.getString("Message"));
        });
    }

    @SysLog
    @ApiOperation(value = "用户启用或禁用")
    @PostMapping("/enable")
    @JwtSecurity(permission = "enable", permissionName = "禁用启用")
    public ResponseEntity<ResponseResult<User>> enable(@Valid @RequestBody BoolIdParams params) throws Exception {
        return userRedisSyncLocker.idLock(params.getTargetId(), () -> {
            User user = service.findOne(params.getTargetId(), "您操作的用户不存在");
            user.setEnable(params.isYes());
            service.save(user);
            return successMessage(user);
        });
    }

    @SysLog
    @ApiOperation(value = "更新用户信息")
    @PostMapping("/update")
    public ResponseEntity<ResponseResult<User>> update(@Valid @RequestBody UserInfoUpdateParams params) {
        final User user = service.findById(JwtUtils.getUser().getId()).orElse(null);
        if (null == user) return errorMessage(HttpStatus.NOT_FOUND.value(), "操作的目标不存在");

        if (null != params.getName()) {
            user.setName(params.getName());
        }
        if (null != params.getAvatar()) {
            user.setAvatar(params.getAvatar());
        }
        if (null != params.getGender()) {
            user.setGender(params.getGender());
        }
        if (null != params.getProvince()) {
            user.setProvince(params.getProvince());
        }
        if (null != params.getCity()) {
            user.setCity(params.getCity());
        }
        if (params.getLongitude() > 0) {
            user.setLongitude(params.getLongitude());
        }
        if (params.getLatitude() > 0) {
            user.setLatitude(params.getLatitude());
        }
        if (params.getInviteUserId() > 0) {
            final User inviteUser = service.findById(params.getInviteUserId()).orElse(null);
            if (null == inviteUser) return errorMessage("邀请码不正确");
        }
        if (params.getBirthday() > 0) {
            user.setBirthday(params.getBirthday());
        }
        service.save(user);

        if (null != params.getExtra()) {
            service.saveUserExtras(user, params.getExtra());
        }

        return successMessage(user);
    }

    @SysLog
    @ApiOperation(value = "账户充值")
    @PostMapping("/recharge")
    @JwtSecurity(permission = "recharge", permissionName = "账户充值")
    public ResponseEntity recharge(@Valid @RequestBody UserRechargeParams params, HttpServletRequest request) throws Exception {
        if (0 == params.getOffset()) return errorMessage("账户充值数额不能为0！");
        if (!redisService.canProceedCustom(String.format("user_recharge_%s", params.getTargetId()), 120)) {
            return errorMessage("为避免重复操作，单人账户充值2分钟内只能操作一次！");
        }
        User user = service.findOne(params.getTargetId());
        Map surplus = wealthFlowService.wealthChange(
                user.getId(),
                params.getOffset(),
                params.getWealthType(),
                OperateType.human, params.getDetail(),
                PayType.surplus, HttpTools.getIp(request),
                JwtUtils.getUser().getId(), UnionType.manager, FlowStatus.success
        );
        return successMessage(surplus);
    }

    private JSONObject getUserSurplus(long userId) {
        JSONObject o = new JSONObject();
        o.put(WealthType.money.name(), wealthFlowService.getSurplus(userId, WealthType.money));
        o.put(WealthType.gold.name(), wealthFlowService.getSurplus(userId, WealthType.gold));
        return o;
    }

    @SysLog
    @ApiOperation(value = "退出登录")
    @PostMapping("/logout")
    public ResponseEntity<ResponseResult<Void>> logout(HttpServletRequest request) throws Exception {
        globalSecurityService.logout(request.getHeader(Global.Constants.TOKEN_KEY), JwtUtils.license(request));
        return successMessage();
    }
}
